// Copyright 2011 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"log"
	"math/rand"
	"os"
	"sort"
	"strings"
	"time"
)

import (
	"walk"
	. "walk/declarative"
)

//一行数据的结构
type Foo struct {
	Index   int
	Bar     string
	Baz     float64
	Quux    time.Time
	checked bool
}

//要填充的数据模型
type FooModel struct {
	walk.TableModelBase		//表格操作对象
	walk.SorterBase		//排序操作对象
	sortColumn int	//列数
	sortOrder  walk.SortOrder	//第几列排序
	items      []*Foo	//多行数据的数组
}

//新建一套填充数据
func NewFooModel() *FooModel {
	m := new(FooModel)
	m.ResetRows()
	return m
}

// Called by the TableView from SetModel and every time the model publishes a
// RowsReset event.
//返回数据的行数量
func (m *FooModel) RowCount() int {
	return len(m.items)
}

// Called by the TableView when it needs the text to display for a given cell.
//返回指定行列单元格的值
func (m *FooModel) Value(row, col int) interface{} {
	item := m.items[row]

	switch col {
	case 0:
		return item.Index

	case 1:
		return item.Bar

	case 2:
		return item.Baz

	case 3:
		return item.Quux
	}

	panic("unexpected col")
}

// Called by the TableView to retrieve if a given row is checked.
//返回指定行是否已被选中
func (m *FooModel) Checked(row int) bool {
	return m.items[row].checked
}

// Called by the TableView when the user toggled the check box of a given row.
//设置指定行的选中状态
func (m *FooModel) SetChecked(row int, checked bool) error {
	m.items[row].checked = checked

	return nil
}

// Called by the TableView to sort the model.
//对指定列进行升序或降序排列
func (m *FooModel) Sort(col int, order walk.SortOrder) error {
	m.sortColumn, m.sortOrder = col, order

	sort.SliceStable(m.items, func(i, j int) bool {
		a, b := m.items[i], m.items[j]

		c := func(ls bool) bool {
			if m.sortOrder == walk.SortAscending {
				return ls
			}

			return !ls
		}

		switch m.sortColumn {
		case 0:
			return c(a.Index < b.Index)

		case 1:
			return c(a.Bar < b.Bar)

		case 2:
			return c(a.Baz < b.Baz)

		case 3:
			return c(a.Quux.Before(b.Quux))
		}

		panic("unreachable")
	})

	return m.SorterBase.Sort(col, order)
}
//重置表格数据
func (m *FooModel) ResetRows() {
	// Create some random data.
	m.items = make([]*Foo, rand.Intn(50))

	now := time.Now()

	for i := range m.items {
		m.items[i] = &Foo{
			Index: i,
			Bar:   strings.Repeat("*", rand.Intn(5)+1),
			Baz:   rand.Float64() * 1000,
			Quux:  time.Unix(rand.Int63n(now.Unix()), 0),
		}
	}

	// Notify TableView and other interested parties about the reset.
	m.PublishRowsReset()

	m.Sort(m.sortColumn, m.sortOrder)
}
//追加行数据
func (m *FooModel) AppendRow()  {
	i := m.RowCount()
	now := time.Now()
	foo := &Foo{
		Index: i,
		Bar:   strings.Repeat("*", rand.Intn(5)+1),
		Baz:   rand.Float64() * 1000,
		Quux:  time.Unix(rand.Int63n(now.Unix()), 0),
	}
	fmt.Printf("追加行数据,id:%d\n",i)
	m.items=append(m.items,foo)
	m.PublishRowsInserted(i,i)
	m.Sort(m.sortColumn, m.sortOrder)
}
func main() {
	logFile, logErr := os.OpenFile("access.log", os.O_CREATE|os.O_RDWR, 0666)
	if logErr != nil {
		fmt.Println("Fail to find", *logFile, "cServer start Failed")
		os.Exit(1)
	}
	log.SetOutput(logFile)
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

	//设置随机数种子
	rand.Seed(time.Now().UnixNano())

	boldFont, _ := walk.NewFont("Segoe UI", 9, walk.FontBold)
	goodIcon, _ := walk.Resources.Icon("../img/check.ico")
	badIcon, _ := walk.Resources.Icon("../img/stop.ico")
	//
	//barBitmap, err := walk.NewBitmap(walk.Size{100, 1})
	//if err != nil {
	//	panic(err)
	//}
	//defer barBitmap.Dispose()
	////
	//canvas, err := walk.NewCanvasFromImage(barBitmap)
	//if err != nil {
	//	panic(err)
	//}
	////
	//_ = canvas.GradientFillRectanglePixels(walk.RGB(255, 0, 0), walk.RGB(0, 255, 0), walk.Horizontal, walk.Rectangle{0, 0, 100, 1})
	//
	//canvas.Dispose()
	//声明一个全局的主窗体对象
	var mw *walk.MainWindow
	model := NewFooModel()	//新建一个数据模型的实体
	//声明一个全局的表格对象
	var tv *walk.TableView



	//设置主窗体并运行
	if err := (MainWindow{
		AssignTo:&mw,
		Title:  "Walk TableView Example",
		Size:   Size{800, 600},
		Layout: VBox{MarginsZero: true},
		Children: []Widget{
			PushButton{
				Text:      "Reset Rows",
				OnClicked: model.ResetRows,
			},
			PushButton{
				Text:      "Append Row",
				OnClicked: model.AppendRow,
			},
			//PushButton{
			//	Text: "Select first 5 even Rows",
			//	OnClicked: func() {
			//		tv.SetSelectedIndexes([]int{0, 2, 4, 6, 8})
			//	},
			//},
			TableView{
				AssignTo:         &tv,
				AlternatingRowBG: true,
				CheckBoxes:       true,
				ColumnsOrderable: true,
				MultiSelection:   true,
				Columns: []TableViewColumn{
					{Title: "操作"},
					{Title: "Bar"},
					{Title: "Baz", Alignment: AlignFar},
					{Title: "Quux", Format: "2006-01-02 15:04:05", Width: 150},
				},
				//设置单元格样式和内容,style为单元格的样式对象
				StyleCell: func(style *walk.CellStyle) {
					//获取数据模型中的行数据
					item := model.items[style.Row()]
					fmt.Println("当前行:",style.Row())
					//如果该行被选中
					if item.checked {
						//如果是偶数行
						if style.Row()%2 == 0 {
							style.BackgroundColor = walk.RGB(159, 215, 255)
						} else {	//如果是奇数行
							style.BackgroundColor = walk.RGB(143, 199, 239)
						}
					}
					//设置列样式和内容,列索引从0开始
					switch style.Col() {
					case 1:	//第二列
						//获取单元格的画布,并重新绘制内容;如果不获取该单元格的画布,就会直接显示数据模型中的数据
						//if canvas := style.Canvas(); canvas != nil {
							//获取单元格边界,调整后传给画布使用
							//bounds := style.Bounds()
							//bounds.X += 2
							//bounds.Y += 2
							//bounds.Width = int((float64(bounds.Width) - 4) / 5 * float64(len(item.Bar)))
							//bounds.Height -= 4
							//根据边界填充背景渐变
							//canvas.DrawBitmapPartWithOpacity(barBitmap, bounds, walk.Rectangle{0, 0, 100 / 5 * len(item.Bar), 1}, 127)

							//bounds.X += 4
							//bounds.Y += 2
							////根据最新的边界绘制文字内容
							//canvas.DrawText(item.Bar, tv.Font(), 0, bounds, walk.TextLeft)
						//
						//}

					case 2:
						//单元格内容大于限定值,就显示绿色文字,并显示勾勾图标
						if item.Baz >= 900.0 {
							style.TextColor = walk.RGB(0, 191, 0)
							style.Image = goodIcon
						} else if item.Baz < 100.0 {
							//如果小于限定值,就用红色显示文字,并显示圈圈图标
							style.TextColor = walk.RGB(255, 0, 0)
							style.Image = badIcon
						}

					case 3:
						//一年之内的时间就用粗体显示
						if item.Quux.After(time.Now().Add(-365 * 24 * time.Hour)) {
							style.Font = boldFont
						}
					}
				},
				Model: model,
				OnSelectedIndexesChanged: func() {	//开启多选功能后,被选中的行
					fmt.Printf("SelectedIndexes: %+v\n", tv.SelectedIndexes())
				},
				OnCurrentIndexChanged: func() {	//行点击后触发
					fmt.Printf("当前选中的行: %v\n", tv.CurrentIndex())
					//获取数据模型中的行数据
					item := model.items[tv.CurrentIndex()]
					item.checked=!item.checked
				},
				OnItemActivated: func() {	//双击的行
					fmt.Printf("ItemActivated: %v\n", tv.SelectedIndexes())
					//第一个参数设置为nil时,产生的是非模态对话框
					walk.MsgBox(mw,"系统提示","双击了该行",walk.MsgBoxOK|walk.MsgBoxIconInformation)
				},
			},
		},

	}).Create(); err != nil {
		log.Fatal(err)
	}
	//给关闭事件增加处理动作
	mw.Closing().Attach(func(canceled *bool, reason walk.CloseReason) {
		reason = walk.CloseReasonUnknown
		ret := walk.MsgBox(mw, "系统提示", "确定要退出吗?", walk.MsgBoxOKCancel|walk.MsgBoxIconQuestion)
		fmt.Println("确认对话框返回值:",ret)

		// 不关闭程序的话,需要对canceled指针重新赋值,以便传递确认退出的二次信号
		*canceled = (ret==2)
		fmt.Printf("是否点击了取消按钮? %t\n",*canceled)

		//使用下面的代码可以将关闭事件的动作广播给其他订阅对象.如果不需要广播,在本函数内完成终结操作也是ok的.
		//var closingPublisher walk.CloseEventPublisher
		//closingPublisher.Publish(canceled, reason)
		// 如果canceled为真就隐藏程序,显示托盘
		//mw.Hide()
		// mw.ni.SetVisible(true)
	})
	mw.Run()
}
